package com.xuecheng.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.xuecheng.api.order.model.dto.OrdersDTO;
import com.xuecheng.api.order.model.vo.PayResultModel;
import com.xuecheng.api.search.model.dto.CoursePubIndexDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.content.CourseChargeEnum;
import com.xuecheng.common.enums.order.OrderDealStatusEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.common.util.PaymentUtil;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.order.agent.SearchApiAgent;
import com.xuecheng.order.common.constant.OrderErrorCode;
import com.xuecheng.order.convert.OrderConvert;
import com.xuecheng.order.entity.Orders;
import com.xuecheng.order.entity.Pay;
import com.xuecheng.order.mapper.OrdersMapper;
import com.xuecheng.order.service.OrdersService;
import com.xuecheng.order.service.PayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements OrdersService {

    @Autowired
    private SearchApiAgent searchApiAgent;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private PayService payService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Value("${order.pay.topic.azhen}")
    private String topicName;


    /**
     * 根据课程发布ID生成和修改课程支付订单
     *
     * @param coursePubId 课程发布ID
     * @return 课程支付订单详情
     * ===业务分析===
     * 1.判断关键数据
     * coursePubId,username
     * 2.获取课程发布的数据
     * 判断课程发布数据是否存在
     * 判断课程是否收费
     * 收费创建订单,如果是免费则不创建
     * 3.生成用户的订单数据
     * 查询用户对该课程是否有订单数据
     * 有:修改订单
     * 判断订单的交易状态
     * 如果是其他状态:订单状态异常
     * 无:创建订单
     * 4.将订单数据转换dto并返回
     */
    @Transactional
    public OrdersDTO createOrModifyOrders(Long coursePubId, String username) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(coursePubId) || StringUtil.isBlank(username)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //2.获取课程发布的数据
        RestResponse<CoursePubIndexDTO> response = searchApiAgent.getCoursePubIndexById4s(coursePubId);

        if (!(response.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(response.getCode(), response.getMsg());
        }
        //2.1判断课程发布数据是否存在
        CoursePubIndexDTO coursePubIndexDTO = response.getResult();

        if (ObjectUtils.isEmpty(coursePubIndexDTO)) {
            ExceptionCast.cast(OrderErrorCode.E_160007);
        }
        //2.2判断课程是否收费
        String charge = coursePubIndexDTO.getCharge();
        if (CourseChargeEnum.CHARGE_NO.getCode().equals(charge)) {
            ExceptionCast.cast(OrderErrorCode.E_160003);
        }


        //3.生成用户的订单数据

        //3.1查询用户对当前课程是否有订单数据
        LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(Orders::getCoursePubId, coursePubId);
        queryWrapper.eq(Orders::getUserName, username);

        Orders orders = this.getOne(queryWrapper);

        if (ObjectUtils.isEmpty(orders)) {
            //如果没有,创建订单
            orders = new Orders();
            //设置订单号
            orders.setOrderNo(PaymentUtil.genUniquePayOrderNo());
            //设置课程数据
            orders.setCoursePubId(coursePubId);
            orders.setCoursePubName(coursePubIndexDTO.getName());
            orders.setCompanyId(coursePubIndexDTO.getCompanyId());

            //设置用户数据
            orders.setUserName(username);

            //设置订单数据
            /*没有优惠券,只考虑交易价格*/
            orders.setPrice(coursePubIndexDTO.getPrice());
            orders.setStartTime(LocalDateTime.now());
            //设置订单的初始状态
            orders.setStatus(new Integer(OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode()));

            orders.setId(null);
            boolean result = this.save(orders);
            if (!result) {
                ExceptionCast.cast(OrderErrorCode.E_160009);
            }


        } else {
            //如果存在,修改订单
            //如果是其他状态,订单状态异常
            if (!(OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode().equals(orders.getStatus().toString()))) {
                ExceptionCast.cast(OrderErrorCode.E_160006);
            }

            //如果是初始态,修改课程最新的价格
            LambdaUpdateWrapper<Orders> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(Orders::getPrice, coursePubIndexDTO.getPrice());
            updateWrapper.set(Orders::getChangeDate, LocalDateTime.now());
            updateWrapper.eq(Orders::getId, orders.getId());
            boolean result = this.update(updateWrapper);
            if (!result) {
                ExceptionCast.cast(OrderErrorCode.E_160012);
            }

            orders = this.getById(orders.getId());

        }
        OrdersDTO ordersDTO = OrderConvert.INSTANCE.entity2dto(orders);
        return ordersDTO;
    }

    /**
     * 微信支付：根据课程订单号 去微信支付系统统一下单，返回codeURL(二维码链接)
     *
     * @param orderNo 课程订单号
     * @return codeURL(二维码链接)以及相应状态
     * ===业务分析===
     * 1.判断关键数据
     * orderNo订单号,username 用户名
     * 2.判断业务数据
     * 查询对应订单是否存在
     * 查询订单的状态
     * 3.生成微信订单支付信息,并将结果封装到请求中
     * 4.获取请求结果并封装
     * 5.生成订单支付数据,保存,并返回结果
     */
    @Override
    @Transactional
    public Map<String, Object> createPay(String orderNo, String username) {
        Map<String, Object> resultMap = new HashMap<>();
        //1.判断关键数据
        if (StringUtil.isBlank(orderNo) || StringUtil.isBlank(username)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //2.判断业务数据
        //查询订单信息
        LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Orders::getOrderNo, orderNo);
        queryWrapper.eq(Orders::getUserName, username);
        //查询处于初始化状态的订单
        String init_statusCode = OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode();
        queryWrapper.eq(Orders::getStatus, new Integer(init_statusCode));
        Orders orders = this.getOne(queryWrapper);
        //判断订单是否为空
        if (ObjectUtils.isEmpty(orders)) {
            ExceptionCast.cast(OrderErrorCode.E_160008);
        }
        //获取课程发布信息
        RestResponse<CoursePubIndexDTO> responseResult = this.searchApiAgent.getCoursePubIndexById4s(orders.getCoursePubId());
        if (!(responseResult.isSuccessful())) {
            ExceptionCast.cast(OrderErrorCode.E_160007);
        }

        CoursePubIndexDTO pubIndexDTO = responseResult.getResult();

        try {
            //创建统一下单请求对象
            WxPayUnifiedOrderRequest unifiedOrderRequest = new WxPayUnifiedOrderRequest();
            // 商品发布
            Long coursePubId = orders.getCoursePubId();
            unifiedOrderRequest.setProductId("购买的课程id为：" + coursePubId);
            // 商品描述
            String description = pubIndexDTO.getDescription();
            if (StringUtil.isBlank(description)) {
                unifiedOrderRequest.setBody("购买课程的信息为：" + pubIndexDTO.getName());
            } else {
                unifiedOrderRequest.setBody(description);
            }
            // 商品订单号
            unifiedOrderRequest.setOutTradeNo(orderNo);
            // 商品金额（单位为：分）
            float price = orders.getPrice() * 100;
            unifiedOrderRequest.setTotalFee((int) price);
            // 生成二维码的终端IP地址
            unifiedOrderRequest.setSpbillCreateIp(InetAddress.getLocalHost().getHostAddress());
            //获取响应
            WxPayUnifiedOrderResult response = this.wxPayService.unifiedOrder(unifiedOrderRequest);
            String returnCode = response.getReturnCode();
            String resultCode = response.getResultCode();

            if (Pay.PAY_SUCCESS_FLAG.equals(returnCode) &&
                    Pay.PAY_SUCCESS_FLAG.equals(resultCode)
            ) {
                String codeURL = response.getCodeURL();
                resultMap.put("codeURL", codeURL);
                resultMap.put("status", Pay.PAY_SUCCESS_STATUS_FLAG);
            } else {
                resultMap.put("status", Pay.PAY_FAILED_STATUS_FLAG);
                resultMap.put("message", OrderErrorCode.E_160014);
                return resultMap;
            }
        } catch (Exception e) {
            log.error("获得第三方支付数据失败,{}", e.getMessage());
            resultMap.put("status", Pay.PAY_FAILED_STATUS_FLAG);
            resultMap.put("message", OrderErrorCode.E_160014);
            return resultMap;
        }
        // 创建订单支付前，先查询是否有订单支付数据（用户的名称和订单id）
        LambdaQueryWrapper<Pay> payQueryWrapper = new LambdaQueryWrapper<>();

        payQueryWrapper.eq(Pay::getOrderId, orders.getId());

        Pay pay = payService.getOne(payQueryWrapper);
        if (ObjectUtils.isEmpty(pay)) {
            pay = new Pay();

            pay.setUserName(username);
            pay.setOrderId(orders.getId());
            pay.setStatus(Pay.PAY_FAILED_STATUS_FLAG);
            pay.setPayMethod(Pay.WX_PAY_METHOD);
            pay.setCompanyId(pubIndexDTO.getCompanyId());
            pay.setTotalAmount(new BigDecimal(orders.getPrice().toString()));

            boolean result = this.payService.save(pay);
            if (!result) {
                // 订单支付数据创建失败返回错误数据
                resultMap.put("message", OrderErrorCode.E_160009.getDesc());
                resultMap.put("status", Pay.PAY_FAILED_STATUS_FLAG);
                resultMap.put("codeURL", "");
            }
        }
        return resultMap;


    }

    /**
     * 支付结果通知并发送事务消息
     *
     * @param xmlPayResult 支付结果
     * @return 接收消息
     * ===业务分析===
     * 1.解析第三方支付通知的结果数据
     * <p>
     * 2.获取支付结果数据并判断
     * 1.根据第三方返回的唯一订单号,获取订单数据(Order)
     * 2.根据order中的id获取订单支付数据(OrderPay)
     * <p>
     * 判断订单数据是否支付
     * Order===>Status
     * OrderPay===>Status
     * 如果订单没有支付的情况下,再发送事务消息
     * 3.发送事务消息
     * 构建消息内容:PayResultModel
     * <p>
     * 4.判断发送事务消息的结果
     * Commit和Rollback
     * <p>
     * 5.返回商家接收通知的结果
     */
    @Override
    public String notifyPayResult(String xmlPayResult) {

        //1.解析第三方支付通知的结果数据
        WxPayOrderNotifyResult notifyResult = null;
        try {
            notifyResult = this.wxPayService.parseOrderNotifyResult(xmlPayResult);
        } catch (WxPayException e) {
            log.error("解析第三方支付通知结果失败：{}", e.getMessage());
            return WxPayNotifyResponse.fail(OrderErrorCode.E_160016.getDesc());
        }
        String returnCode = notifyResult.getReturnCode();

        if (Pay.PAY_SUCCESS_FLAG.equals(returnCode)) {
            //获取支付成功的消息
            //2.获取支付结果数据并判断
            //订单号
            String orderNo = notifyResult.getOutTradeNo();

            //* 1.根据第三方返回的唯一订单号,获取订单数据(Order)
            LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Orders::getOrderNo, orderNo);
            Orders orders = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(orders)) {
                //获取支付失败的消息
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160008.getDesc());
            }
            //* 2.根据order中的订单号获取订单支付数据(OrderPay)
            LambdaQueryWrapper<Pay> queryPayWrapper = new LambdaQueryWrapper<>();
            queryPayWrapper.eq(Pay::getOrderId, orders.getId());
            Pay pay = this.payService.getOne(queryPayWrapper);
            if (ObjectUtils.isEmpty(pay)) {
                //获取支付失败的消息
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160015.getDesc());
            }
            //判断订单是否支付Order===>Status
            if (!(OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode().equals(orders.getStatus().toString()))) {
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160006.getDesc());
            }
            // 判断课程支付是否支付 OrderPay===>Status
            if (!(Pay.PAY_FAILED_STATUS_FLAG.equals(pay.getStatus()))) {
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160006.getDesc());
            }
            //3.发送事务消息
            //构建事务消息 PayResultModel
            PayResultModel resultModel = new PayResultModel();
            //1.学成订单
            resultModel.setOrder(OrderConvert.INSTANCE.entity2dto(orders));
            //2.支付方式
            resultModel.setPayMethod(notifyResult.getTradeType());
            //3.支付时间
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            String timeEnd = notifyResult.getTimeEnd();
            LocalDateTime payDate = LocalDateTime.parse(timeEnd, dateTimeFormatter);
            resultModel.setPayDate(payDate);
            //4.微信支付订单号
            String tradeNo = notifyResult.getTransactionId();
            resultModel.setPayNumber(tradeNo);
            //5.第三方支付响应结果
            resultModel.setPayResponse(notifyResult.getXmlString());
            //6.交易状态
            resultModel.setTradeStatus(notifyResult.getResultCode());
            //7.设置金额
            //订单总金额
            String total = BaseWxPayResult.fenToYuan(notifyResult.getTotalFee());
            resultModel.setTotalAmount(new BigDecimal(total));
            //订单应付金额(优惠券业务会使用此数据)
            Integer settlementTotalFee = notifyResult.getSettlementTotalFee();
            if (!ObjectUtils.isEmpty(settlementTotalFee)) {
                String receiptAmount = BaseWxPayResult.fenToYuan(settlementTotalFee);
                resultModel.setReceiptAmount(new BigDecimal(receiptAmount));
            }

            //订单实付金额(优惠券业务会使用此数据)
            Integer payAmount = notifyResult.getCashFee();
            if (!ObjectUtils.isEmpty(payAmount)) {
                String cashAmount = BaseWxPayResult.fenToYuan(payAmount);
                resultModel.setBuyerPayAmount(new BigDecimal(cashAmount));
            }

            //发送事务消息
            String payJson = JsonUtil.objectTojson(resultModel);
            Message<String> message = MessageBuilder.withPayload(payJson).build();

            TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(topicName, message, null);

            if (!(LocalTransactionState.COMMIT_MESSAGE.equals(result.getLocalTransactionState()))) {
                log.error("发送订单支付事务消息失败");
                return WxPayNotifyResponse.fail(OrderErrorCode.E_160011.getDesc());
            }

        } else {
            //获取支付失败的消息
            return WxPayNotifyResponse.fail(notifyResult.getReturnMsg());
        }

        return WxPayNotifyResponse.success(notifyResult.getReturnMsg());
    }

    /**
     * 订单支付通知,执行本地事务,修改付款状态
     *
     * @param resultModel 响应结果
     *  ===业务分析===
     *  1.判断业务数据
     *  订单===>Orders
     *  订单支付===>OrderPay
     *  考虑消息的幂等性
     *  1.1判断订单数据----PayResultModel:OrderDTO
     *  是否存在
     *  支付状态
     *  1.2判断订单支付数据
     *  是否存在
     *  支付状态
     *  2.修改订单的相关数据
     *  支付时间
     *  实收金额
     *  付款金额
     *  支付的响应结果
     */
    @Override
    @Transactional
    public void successPayment(PayResultModel resultModel) {
        OrdersDTO ordersDTO = resultModel.getOrder();
        //* 1.判断订单数据
        Orders orders = this.getById(ordersDTO.getOrderId());
        if (ObjectUtils.isEmpty(orders)) {
            //获取支付失败的消息
            ExceptionCast.cast(OrderErrorCode.E_160008);
        }
        //* 2.根据order中的订单号获取订单支付数据(OrderPay)
        LambdaQueryWrapper<Pay> queryPayWrapper = new LambdaQueryWrapper<>();
        queryPayWrapper.eq(Pay::getOrderId, orders.getId());
        Pay pay = this.payService.getOne(queryPayWrapper);
        if (ObjectUtils.isEmpty(pay)) {
            //获取支付失败的消息
            ExceptionCast.cast(OrderErrorCode.E_160015);
        }
        //判断订单是否支付Order===>Status
        if (OrderDealStatusEnum.ORDER_DEAL_PAID_STATUS.getCode().equals(orders.getStatus().toString())) {
            ExceptionCast.cast(OrderErrorCode.E_160006);
        }
        // 判断课程支付是否支付 OrderPay===>Status
        if (!(Pay.PAY_FAILED_STATUS_FLAG.equals(pay.getStatus()))) {
            ExceptionCast.cast(OrderErrorCode.E_160006);
        }
        //2.修改订单的相关数据
        //第三方订单必须为支付成功,才能修改数据
        if (Pay.PAY_SUCCESS_FLAG.equals(resultModel.getTradeStatus())) {
            //Order===>Status修改为已支付
            LambdaUpdateWrapper<Orders> OrderUpdateWrapper = new LambdaUpdateWrapper<>();
            OrderUpdateWrapper.set(Orders::getStatus, OrderDealStatusEnum.ORDER_DEAL_PAID_STATUS.getCode());
            OrderUpdateWrapper.set(Orders::getChangeDate, LocalDateTime.now());
            OrderUpdateWrapper.set(Orders::getChangeDate, LocalDateTime.now());
            OrderUpdateWrapper.eq(Orders::getId, orders.getId());
            boolean result = this.update(OrderUpdateWrapper);

            if (!result) {
                ExceptionCast.cast(OrderErrorCode.E_160013);
            }
            //OrderPay===>Status修改为已支付
            //设置交易状态
            pay.setStatus(Pay.PAY_SUCCESS_STATUS_FLAG);
            //设置支付时间
            pay.setPayDate(resultModel.getPayDate());
            //实收金额
            pay.setReceiptAmount(resultModel.getReceiptAmount());
            //付款金额
            pay.setBuyerPayAmount(resultModel.getBuyerPayAmount());
            //支付的响应结果
            pay.setPayResponse(resultModel.getPayResponse());
            //设置微信支付订单号
            pay.setPayNumber(resultModel.getPayNumber());


            boolean payResult = this.payService.updateById(pay);
            if (!payResult) {
                ExceptionCast.cast(OrderErrorCode.E_160012);
            }


        } else {
            ExceptionCast.cast(OrderErrorCode.E_160017);
        }


    }
}
